// CS3241Lab1.cpp : Defines the entry point for the console application.
//#include <cmath>
//#include "math.h"
#include <iostream>
#include <fstream>
#include <vector>
//gl headers for xcode
#include <OpenGL/gl.h>
#include <GLUT/GLUT.h>

#define _USE_MATH_DEFINES
#include <math.h>

using namespace std;

#define NO_OBJECT 4
#define ISCALE 250.0
#define NLINESEGMENT 20
#define NCIRCLESEGMENT 30

// global variable
// pi value can be accessed using the built-in M_PI
bool m_Smooth = false;
bool m_Highlight = false;
float no_shininess = 0.0f;
float no_mat[4] = {0,0,0,1};

GLfloat angle = 0;   /* in degrees */
GLfloat angle2 = 0;   /* in degrees */
GLfloat zoom = 1.0;
int mouseButton = 0;
int moving, startx, starty;
int current_object = 0;

//structure of integer point pair
struct PointD {
	int x,y;
};

//structure of float point pair
struct PointF {
	double x,y;
    
	PointF(double _x, double _y)
	{
		x = _x;
		y = _y;
	}
    
	PointF()
	{
		x = y = 0;
	}
};

struct Material
{
	float mat_ambient[4];
	float mat_diffuse[4];
	float specular[4];
	float shininess;
};

struct vec
{
    double x, y, z;
    double v[3];
    
    vec(double a, double b, double c)
    {
        x = v[0] = a;
        y = v[1] = b;
        z = v[2] = c;
    };
    
    vec()
    {
    };
    
    void update()
    {
        v[0] = x;
        v[1] = y;
        v[2] = z;
    };
    
    void clear()
    {
        x = y = z = 0;
        v[0] = v[1] = v[2] = 0;
    }
};

vec list[10000][NCIRCLESEGMENT + 5];

Material Jade, MyMaterial, MyPearl, MyRose, MyRoseOuter;

double cx, cy, cz, ck, calpha;

int nPt;
PointD ptList[100];
PointF ptListR[100], ptListL[100], ptListP[100];

vector<PointF> b2D;

void setMat()
{
	MyMaterial.mat_ambient[0] = 0.831250;
	MyMaterial.mat_ambient[1] = 0.831250;
	MyMaterial.mat_ambient[2] = 0.831250;
	MyMaterial.mat_ambient[3] = 1.000000;
    
	MyMaterial.mat_diffuse[0] = 0.277500;
	MyMaterial.mat_diffuse[1] = 0.277500;
	MyMaterial.mat_diffuse[2] = 0.277500;
	MyMaterial.mat_diffuse[3] = 1.000000;
    
	MyMaterial.specular[0] = 0.773911;
	MyMaterial.specular[1] = 0.773911;
	MyMaterial.specular[2] = 0.773911;
	MyMaterial.specular[3] = 1.000000;
    
	MyMaterial.shininess = 89.600000;
    
    MyPearl.mat_ambient[0] = 0.731250;
	MyPearl.mat_ambient[1] = 0.731250;
	MyPearl.mat_ambient[2] = 0.731250;
	MyPearl.mat_ambient[3] = 1.000000;
    
	MyPearl.mat_diffuse[0] = 0.277500;
	MyPearl.mat_diffuse[1] = 0.277500;
	MyPearl.mat_diffuse[2] = 0.277500;
	MyPearl.mat_diffuse[3] = 1.000000;
    
	MyPearl.specular[0] = 0.773911;
	MyPearl.specular[1] = 0.773911;
	MyPearl.specular[2] = 0.773911;
	MyPearl.specular[3] = 1.000000;
    
	MyPearl.shininess = 89.600000;
    
    MyRose.mat_ambient[0] = 233/ 255.0;
	MyRose.mat_ambient[1] = 66 / 255.0;
	MyRose.mat_ambient[2] = 66 / 255.0;
    MyRose.mat_ambient[3] = 1.000000;
    
	MyRose.mat_diffuse[0] = 0.277500;
	MyRose.mat_diffuse[1] = 0.277500;
	MyRose.mat_diffuse[2] = 0.277500;
	MyRose.mat_diffuse[3] = 1.000000;
    
	MyRose.specular[0] = 0.773911;
	MyRose.specular[1] = 0.773911;
	MyRose.specular[2] = 0.773911;
	MyRose.specular[3] = 1.000000;
    
	MyRose.shininess = 89.600000;
    
    MyRoseOuter.mat_ambient[0] = 213/ 255.0;
	MyRoseOuter.mat_ambient[1] = 52 / 255.0;
	MyRoseOuter.mat_ambient[2] = 52 / 255.0;
    MyRoseOuter.mat_ambient[3] = 1.000000;
    
	MyRoseOuter.mat_diffuse[0] = 0.277500;
	MyRoseOuter.mat_diffuse[1] = 0.277500;
	MyRoseOuter.mat_diffuse[2] = 0.277500;
	MyRoseOuter.mat_diffuse[3] = 1.000000;
    
	MyRoseOuter.specular[0] = 0.773911;
	MyRoseOuter.specular[1] = 0.773911;
	MyRoseOuter.specular[2] = 0.773911;
	MyRoseOuter.specular[3] = 1.000000;
    
	MyRoseOuter.shininess = 89.600000;
    
	Jade.mat_ambient[0] = 0.135000;
	Jade.mat_ambient[1] = 0.222500;
	Jade.mat_ambient[2] = 0.157500;
	Jade.mat_ambient[3] = 0.950000;
    
	Jade.mat_diffuse[0] = 0.540000;
	Jade.mat_diffuse[1] = 0.890000;
	Jade.mat_diffuse[2] = 0.630000;
	Jade.mat_diffuse[3] = 0.950000;
    
	Jade.specular[0] = 0.316228;
	Jade.specular[1] = 0.316228;
	Jade.specular[2] = 0.316228;
	Jade.specular[3] = 0.950000;
    
	Jade.shininess = 12.800000;
}

/**
 * This function is called when the point list is in a sepearte file. It will
 * store the point pairs in ptList, which is an integer point pair array.
 *
 * @param fileName[]
 *			is the file name of the target files
 */
void readFile(char fileName[])
{
	freopen(fileName, "r", stdin);
	scanf("%d", &nPt);
    
	memset(ptList, 0, sizeof(ptList));
    
	for (int i = 0; i < nPt; i ++)
	{
		scanf("%d%d", &ptList[i].x, &ptList[i].y);
	}
}

/**
 * This function will convert all integer point pairs in ptList to float point
 * pairs and store them inside an array, which is specified by parameter list[].
 * Besides, the function will scale all the points into the size of current
 * window.
 */
void convertAndScale(PointF list[])
{
	memset(list, 0, sizeof(list));
    
	for (int i = 0; i < nPt; i ++)
	{
		list[i].x = (double)ptList[i].x / ISCALE;
		list[i].y = - (double)ptList[i].y / ISCALE;
	}
}

/**
 * This function is used to get a 2D beizer curve.
 *
 * @param ptList[]
 *			is the control point list of the beizier curve to be drawn;
 */
void getBeizer2D(PointF ptList[100])
{
	for(int i = 0;i < nPt; i++)
	{
//        printf("%.2lf %.2lf\n", ptList[i].x, ptList[i].y);
		if(i % 3 == 0 && i != 0)
		{
            for(float j =0; j <= 1; j += (float (1)/float(NLINESEGMENT)))
                b2D.push_back(PointF(ptList[i-3].x * (1-j) * (1-j) * (1-j) +
                                     3 * ptList[i-2].x * (1-j) * (1-j) * j +
                                     3 * ptList[i-1].x * (1-j) * j * j + ptList[i].x * j * j * j,
                                     ptList[i-3].y * (1-j) * (1-j) * (1-j) +
                                     3 * ptList[i-2].y * (1-j) * (1-j) * j +
                                     3 * ptList[i-1].y * (1-j) * j * j + ptList[i].y * j * j * j));
		}
	}
}

void drawBeizerCurve3D(double r, Material mat)
{    
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat.mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.mat_diffuse);
    
	if(m_Highlight)
	{
		// your codes for highlight here
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specular);
		glMaterialf(GL_FRONT, GL_SHININESS, mat.shininess);
        
	} else {
		glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
		glMaterialf(GL_FRONT, GL_SHININESS, no_shininess);
	}
    
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    
    int size = (int)b2D.size();
    b2D.push_back(b2D[0]);
    
    double theta = 2 * M_PI / NCIRCLESEGMENT;
    double cur = 0;
    
    for (int i = 0; i < size; i ++)
    {
        for (int j = 0; j < NCIRCLESEGMENT; j ++)
        {
            glBegin(GL_POLYGON);
            if (m_Smooth)
            {
                glNormal3d(r * sin(cur), 0, r * cos(cur));
                glVertex3d(b2D[i].x + r * sin(cur), b2D[i].y, r * cos(cur));
                glNormal3d(r * sin(cur), 0, r * cos(cur));
                glVertex3d(b2D[i + 1].x + r * sin(cur), b2D[i + 1].y, r * cos(cur));
                cur += theta;
                if (i + 1 == size) cur = 0;
                glNormal3d(r * sin(cur), 0, r * cos(cur));
                glVertex3d(b2D[i + 1].x + r * sin(cur), b2D[i + 1].y, r * cos(cur));
                glNormal3d(r * sin(cur), 0, r * cos(cur));
                glVertex3d(b2D[i].x + r * sin(cur), b2D[i].y, r * cos(cur));
                glEnd();
            } else
            {
                glNormal3d(r * (sin(cur) + sin(cur + theta)), 0, r * (cos(cur) + cos(cur + theta)));
                
                glVertex3d(b2D[i].x + r * sin(cur), b2D[i].y, r * cos(cur));
                glVertex3d(b2D[i + 1].x + r * sin(cur), b2D[i + 1].y, r * cos(cur));
                cur += theta;
                if (i + 1 == size) cur = 0;
                glVertex3d(b2D[i + 1].x + r * sin(cur), b2D[i + 1].y, r * cos(cur));
                glVertex3d(b2D[i].x + r * sin(cur), b2D[i].y, r * cos(cur));
                glEnd();
            }
        }
    }
}

//v3 = v1 + v2; (v1, v2, v3 are vectors)
vec plusv(vec v1, vec v2)
{
    vec t;
    
    t.x = v1.x + v2.x;
    t.y = v1.y + v2.y;
    t.z = v1.z + v2.z;
    
    t.update();
    
    return t;
}

//v3 = v1 - v2; (v1, v2, v3 are vectors)
vec minusv(vec v1, vec v2)
{
    vec t;
    
    t.x = v1.x - v2.x;
    t.y = v1.y - v2.y;
    t.z = v1.z - v2.z;
    
    t.update();
    
    return t;
}

//v3 = v1 X v2; (cross product of v1 and v2)
vec crossv(vec v1, vec v2)
{
    vec t;
    
    t.x = v1.y * v2.z - v1.z * v2.y;
    t.y = v1.z * v2.x - v1.x * v2.z;
    t.z = v1.x * v2.y - v1.y * v2.x;
    
    t.update();
    
    return t;
}

//a surface with r from the origin, spanning angle angle
void drawBeizerSurface(double rBase, double deltaR, double angle, Material mat)
{    
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat.mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.mat_diffuse);
    
	if(m_Highlight)
	{
		// your codes for highlight here
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specular);
		glMaterialf(GL_FRONT, GL_SHININESS, mat.shininess);
        
	} else {
		glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
		glMaterialf(GL_FRONT, GL_SHININESS, no_shininess);
	}
    
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    
    double cur = 0;
    double delta = angle / NCIRCLESEGMENT;
    memset(list, 0, sizeof(list));
    
    int size = (int)b2D.size();
    
    //first time to get the normal vector for each vertex
    for (int i = 0; i < NCIRCLESEGMENT; i ++, cur += delta)
    {
        double r = rBase;
        
        for (int j = 0; j < size - 1; j ++)
        {
            vec vec1(b2D[j].x + r * sin(cur), b2D[j].y, r * cos(cur));
            vec vec4(b2D[j].x + r * sin(cur + delta), b2D[j].y, r * cos(cur + delta));
            
            r += deltaR;
            
            vec vec2(b2D[j + 1].x + r * sin(cur), b2D[j + 1].y, r * cos(cur));
            vec vec3(b2D[j + 1].x + r * sin(cur + delta), b2D[j + 1].y, r * cos(cur + delta));
            
            vec vec5 = crossv(minusv(vec3, vec2), minusv(vec4, vec3));
            
            //cumulating normal vecs for each vertex
            list[j][i] = plusv(list[j][i], vec5);
            list[j + 1][i] = plusv(list[j + 1][i], vec5);
            list[j][i + 1] = plusv(list[j][i + 1], vec5);
            list[j + 1][i + 1] = plusv(list[j + 1][i + 1], vec5);
            
            if (!m_Smooth)
            {
                glBegin(GL_POLYGON);
                glNormal3dv(vec5.v);
                glVertex3dv(vec1.v);
                glVertex3dv(vec2.v);
                glVertex3dv(vec3.v);
                glVertex3dv(vec4.v);
                glEnd();
            }
        }
    }
    
    cur = 0;

    //if we're in smooth mode, we need second time to draw polygons
    if (m_Smooth)
    {
        for (int i = 0; i < NCIRCLESEGMENT; i ++, cur += delta)
        {
            double r = rBase;
            
            for (int j = 0; j < size - 1; j ++)
            {
                vec vec1(b2D[j].x + r * sin(cur), b2D[j].y, r * cos(cur));
                vec vec4(b2D[j].x + r * sin(cur + delta), b2D[j].y, r * cos(cur + delta));
                
                r += deltaR;
                
                vec vec2(b2D[j + 1].x + r * sin(cur), b2D[j + 1].y, r * cos(cur));
                vec vec3(b2D[j + 1].x + r * sin(cur + delta), b2D[j + 1].y, r * cos(cur + delta));
                
                glBegin(GL_POLYGON);
                glNormal3dv(list[j][i].v);
                glVertex3dv(vec1.v);
                glNormal3dv(list[j + 1][i].v);
                glVertex3dv(vec2.v);
                glNormal3dv(list[j + 1][i + 1].v);
                glVertex3dv(vec3.v);
                glNormal3dv(list[j][i + 1].v);
                glVertex3dv(vec4.v);
                glEnd();
            }
        }
    }
}

void setupLighting()
{
	glShadeModel(GL_SMOOTH);
	glEnable(GL_NORMALIZE);
    
	// Lights, material properties
    GLfloat	ambientProperties[]  = {0.7f, 0.7f, 0.7f, 1.0f};
	GLfloat	diffuseProperties[]  = {0.8f, 0.8f, 0.8f, 1.0f};
    GLfloat	specularProperties[] = {1.0f, 1.0f, 1.0f, 1.0f};
	GLfloat lightPosition[] = {-100.0f,100.0f,100.0f,1.0f};
	
	
    glClearDepth( 1.0 );
    
	glLightfv( GL_LIGHT0, GL_POSITION, lightPosition);
	
    glLightfv( GL_LIGHT0, GL_AMBIENT, ambientProperties);
    glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseProperties);
    glLightfv( GL_LIGHT0, GL_SPECULAR, specularProperties);
    glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0.0);
    
	// Default : lighting
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);
    
}

void drawSphere(double r, Material mat)
{
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat.mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.mat_diffuse);
    
	if(m_Highlight)
	{
		// your codes for highlight here
		glMaterialfv(GL_FRONT,GL_SPECULAR, mat.specular);
		glMaterialf(GL_FRONT, GL_SHININESS, mat.shininess);
	} else {
		glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
		glMaterialf(GL_FRONT, GL_SHININESS, no_shininess);
	}
    
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
	
    int i,j;
	int n = 20;
    for(i=0;i<n;i++)
		for(j=0;j<2*n;j++)
            if(m_Smooth)
            {
                glBegin(GL_POLYGON);
                
			    // the normal of each vertex is actaully its own coordinates normalized for a sphere
                
			    // your normal here
				glNormal3d(r*sin(i*M_PI/n)*cos(j*M_PI/n),r*cos(i*M_PI/n)*cos(j*M_PI/n),r*sin(j*M_PI/n));
				glVertex3d(r*sin(i*M_PI/n)*cos(j*M_PI/n),r*cos(i*M_PI/n)*cos(j*M_PI/n),r*sin(j*M_PI/n));
			    // your normal here
				glNormal3d(r*sin((i+1)*M_PI/n)*cos(j*M_PI/n),r*cos((i+1)*M_PI/n)*cos(j*M_PI/n),r*sin(j*M_PI/n));
				glVertex3d(r*sin((i+1)*M_PI/n)*cos(j*M_PI/n),r*cos((i+1)*M_PI/n)*cos(j*M_PI/n),r*sin(j*M_PI/n));
			    // your normal here
				glNormal3d(r*sin((i+1)*M_PI/n)*cos((j+1)*M_PI/n),r*cos((i+1)*M_PI/n)*cos((j+1)*M_PI/n),r*sin((j+1)*M_PI/n));
				glVertex3d(r*sin((i+1)*M_PI/n)*cos((j+1)*M_PI/n),r*cos((i+1)*M_PI/n)*cos((j+1)*M_PI/n),r*sin((j+1)*M_PI/n));
			    // your normal here
				glNormal3d(r*sin(i*M_PI/n)*cos((j+1)*M_PI/n),r*cos(i*M_PI/n)*cos((j+1)*M_PI/n),r*sin((j+1)*M_PI/n));
				glVertex3d(r*sin(i*M_PI/n)*cos((j+1)*M_PI/n),r*cos(i*M_PI/n)*cos((j+1)*M_PI/n),r*sin((j+1)*M_PI/n));
                glEnd();
            } else	{
                glBegin(GL_POLYGON);
			    // Explanation: the normal of the whole polygon is the coordinate of the center of the polygon for a sphere
			    glNormal3d(sin((i+0.5)*M_PI/n)*cos((j+0.5)*M_PI/n),cos((i+0.5)*M_PI/n)*cos((j+0.5)*M_PI/n),sin((j+0.5)*M_PI/n));
				glVertex3d(r*sin(i*M_PI/n)*cos(j*M_PI/n),r*cos(i*M_PI/n)*cos(j*M_PI/n),r*sin(j*M_PI/n));
				glVertex3d(r*sin((i+1)*M_PI/n)*cos(j*M_PI/n),r*cos((i+1)*M_PI/n)*cos(j*M_PI/n),r*sin(j*M_PI/n));
				glVertex3d(r*sin((i+1)*M_PI/n)*cos((j+1)*M_PI/n),r*cos((i+1)*M_PI/n)*cos((j+1)*M_PI/n),r*sin((j+1)*M_PI/n));
				glVertex3d(r*sin(i*M_PI/n)*cos((j+1)*M_PI/n),r*cos(i*M_PI/n)*cos((j+1)*M_PI/n),r*sin((j+1)*M_PI/n));
                glEnd();
            }
    
}

void drawTorus(float c, float a, float s, Material mat)
{
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat.mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.mat_diffuse);

	if(m_Highlight)
	{
		// your codes for highlight here
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specular);
		glMaterialf(GL_FRONT, GL_SHININESS, mat.shininess);

	} else {
		glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
		glMaterialf(GL_FRONT, GL_SHININESS, no_shininess);
	}

    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
	glBegin(GL_POLYGON);
	int n = 20;
	int i,j;//x,y,z;
		for(j = 0; j < s*2*n; j++)
		{
			for(i = 0; i < 2*n; i++)
			{
				if(m_Smooth)
				{
					glBegin(GL_POLYGON);
						glNormal3d(cos(i * M_PI/n) * cos(j * M_PI/n), cos(i * M_PI/n) * sin(j * M_PI/n), sin(i * M_PI/n));
						glVertex3d((c + a * cos(i * M_PI/n)) * cos(j * M_PI/n), (c + a * cos(i * M_PI/n)) * sin(j * M_PI/n), a * sin(i * M_PI/n));

						glNormal3d(cos(i * M_PI/n) * cos((j+1) * M_PI/n), cos(i * M_PI/n) * sin((j+1) * M_PI/n), sin(i * M_PI/n));
						glVertex3d((c + a * cos(i * M_PI/n)) * cos((j+1) * M_PI/n), (c + a * cos(i * M_PI/n)) * sin((j+1) * M_PI/n), a * sin(i * M_PI/n));

						glNormal3d(cos((i+1) * M_PI/n) * cos((j+1) * M_PI/n), cos((i+1) * M_PI/n) * sin((j+1) * M_PI/n), sin((i+1) * M_PI/n));
						glVertex3d((c + a * cos((i+1) * M_PI/n)) * cos((j+1) * M_PI/n), (c + a * cos((i+1) * M_PI/n)) * sin((j+1) * M_PI/n), a * sin((i+1) * M_PI/n));

						glNormal3d(cos((i+1) * M_PI/n) * cos(j * M_PI/n), cos((i+1) * M_PI/n) * sin(j * M_PI/n), sin((i+1) * M_PI/n));
						glVertex3d((c + a * cos((i+1) * M_PI/n)) * cos(j * M_PI/n), (c + a * cos((i+1) * M_PI/n)) * sin(j * M_PI/n), a * sin((i+1) * M_PI/n));
					glEnd();
				}
				else
				{
					glBegin(GL_POLYGON);
						glNormal3d(cos((i+0.5) * M_PI/n) * cos((j+0.5) * M_PI/n), cos((i+0.5) * M_PI/n) * sin((j+0.5) * M_PI/n), sin((i+0.5) * M_PI/n));
						glVertex3d((c + a * cos(i * M_PI/n)) * cos(j * M_PI/n), (c + a * cos(i * M_PI/n)) * sin(j * M_PI/n), a * sin(i * M_PI/n));
						glVertex3d((c + a * cos((i+1) * M_PI/n)) * cos(j * M_PI/n), (c + a * cos((i+1) * M_PI/n)) * sin(j * M_PI/n), a * sin((i+1) * M_PI/n));
						glVertex3d((c + a * cos((i+1) * M_PI/n)) * cos((j+1) * M_PI/n), (c + a * cos((i+1) * M_PI/n)) * sin((j+1) * M_PI/n), a * sin((i+1) * M_PI/n));
						glVertex3d((c + a * cos(i * M_PI/n)) * cos((j+1) * M_PI/n), (c + a * cos(i * M_PI/n)) * sin((j+1) * M_PI/n), a * sin(i * M_PI/n));
					glEnd();
				}
			}
		}
	glEnd();
}

void drawWing()
{
    glPushMatrix();
    glTranslatef(-1.8, 0.8, 0);
    
    glPushMatrix();
    glScalef(-0.1, -0.1, -0.1);
    
    drawTorus(1, 0.3, 1, MyMaterial);
    glPopMatrix();
    
    glRotatef(90, 1, 0, 0);
    glRotatef(-45, 0, 1, 0);
    glTranslatef(-0.15, 0, 0);
    
    glPushMatrix();
    glScalef(0.1, 0.1, 0.1);
    drawTorus(1, 0.2, 1, MyMaterial);
    glPopMatrix();
    
    for (int i = 0; i < 20; i ++)
    {
        glTranslatef(-0.07, 0, 0);
        glRotatef(90, 1, 0, 0);
        
        glPushMatrix();
        glScalef(0.05, 0.05, 0.05);
        drawTorus(1, 0.2, 1, MyMaterial);
        glPopMatrix();
    }
    
    glPopMatrix();
    
    glPushMatrix();
    glTranslatef(-2.8, 1.7, 0);
    glScalef(1.4, 1.4, 1.4);
    
    drawBeizerCurve3D(0.03, MyMaterial);
    glPopMatrix();
}

void drawNecklace()
{
    readFile("wing3.txt");
    convertAndScale(ptListP);
    
    b2D.clear();
    
    glPushMatrix();
    
    getBeizer2D(ptListP);
    
    drawWing();

    glPushMatrix();
    glRotatef(180, 0, 1, 0);

    drawWing();
    glPopMatrix();
    
    glPushMatrix();
    
    glTranslatef(0, -0.7, 0);
    glScalef(0.4, 0.4, 0.4);
    
    drawTorus(1, 0.1, 1, MyMaterial);
    glPopMatrix();
    
    glPushMatrix();
    glTranslatef(0, -0.7, 0);
    glScalef(0.366, 0.366, 0.366);
    drawSphere(1, MyPearl);
    glPopMatrix();
    
    glPushMatrix();
    glTranslatef(0, -0.5, 0);
    glScalef(0.35, 0.35, 0.35);
    glRotatef(-90, 1, 0, 0);
    
    drawTorus(1, 0.1, 1, MyMaterial);
    glPopMatrix();
    
	glPopMatrix();
}

void drawCylinder(double r, double height, Material mat)
{
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat.mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.mat_diffuse);
    
	if(m_Highlight)
	{
		// your codes for highlight here
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specular);
		glMaterialf(GL_FRONT, GL_SHININESS, mat.shininess);
        
	} else {
		glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
		glMaterialf(GL_FRONT, GL_SHININESS, no_shininess);
	}
    
    glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
    
    double cur = 0;
    double delta = 1 * M_PI / NLINESEGMENT;
    
    for (int i = 0; i < 2 * NLINESEGMENT; i ++)
    {
        if (m_Smooth)
        {
            glBegin(GL_POLYGON);
            glNormal3d(r * sin(cur), 0, r * cos(cur));
            glVertex3d(r * sin(cur), 0, r * cos(cur));
            glNormal3d(r * sin(cur), 0, r * cos(cur));
            glVertex3d(r * sin(cur), height, r * cos(cur));
            
            cur += delta;
            glNormal3d(r * sin(cur), 0, r * cos(cur));
            glVertex3d(r * sin(cur), height, r * cos(cur));
            glNormal3d(r * sin(cur), 0, r * cos(cur));
            glVertex3d(r * sin(cur), 0, r * cos(cur));
            glEnd();
        } else
        {
            glNormal3d(r * sin(cur + delta / 2), 0, r * cos(cur + delta / 2));
            glBegin(GL_POLYGON);
            glVertex3d(r * sin(cur), 0, r * cos(cur));
            glVertex3d(r * sin(cur), height, r * cos(cur));
            
            cur += delta;
            
            glVertex3d(r * sin(cur), height, r * cos(cur));
            glVertex3d(r * sin(cur), 0, r * cos(cur));
            glEnd();
        }
    }
}

void drawLeaf(Material mat)
{
    glPushMatrix();
    glRotated(-174, 0, 1, 0);
    glTranslated(-1.3, -1.5, -0.1);
    glScaled(1, 0.2, 1);
    drawBeizerSurface(0.5, -0.02, 125 * M_PI / 180.0, mat);
    glPopMatrix();
    
    glPushMatrix();
    glRotated(12, 0, 1, 0);
    glTranslated(-1.3, -1.2, 0);
    glScaled(1, 1.2, 1);
    drawBeizerSurface(0.5, -0.02, 125 * M_PI / 180.0, mat);
    glPopMatrix();
}

void drawRose()
{
    readFile("rose3.txt");
    convertAndScale(ptListP);
    
    b2D.clear();

    getBeizer2D(ptListP);
    
    glPushMatrix();
    glRotated(-120, 0, 1, 0);
    glTranslated(-1.6, 1.4, 0);
    drawBeizerSurface(0.35, 0.01, 150 * M_PI / 180.0, MyRose);
    glPopMatrix();
    
    glPushMatrix();
    glRotated(-214, 0, 1, 0);
    glTranslated(-1.5, 1.6, 0);
    glScaled(1, 1.1, 1);
    drawBeizerSurface(0.35, 0.01, 150 * M_PI / 180.0, MyRose);
    glPopMatrix();
    
    glPushMatrix();
    glRotated(-1, 0, 1, 0);
    glTranslated(-1.5, 1.5, 0);
    drawBeizerSurface(0.35, 0.01, 175 * M_PI / 180.0, MyRose);
    glPopMatrix();

    readFile("rose1.txt");
    convertAndScale(ptListP);
    
    b2D.clear();
    
    getBeizer2D(ptListP);
    
    for (int i = 0; i < 5; i ++)
    {
        glPushMatrix();
        glTranslated(-1.2, 0.8, 0);
        drawBeizerSurface(0.5, 0, 125 * M_PI / 180.0, MyRoseOuter);
        glPopMatrix();
        glRotated(70, 0, 1, 0);
    }
    
    drawLeaf(Jade);
    
    glPushMatrix();
    glTranslated(0, -5, 0);
    drawCylinder(0.07, 5, Jade);
    glPopMatrix();
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glPushMatrix();
    glTranslatef(0, 0, -6);
    
    glRotatef(angle2, 1.0, 0.0, 0.0);
    glRotatef(angle, 0.0, 1.0, 0.0);
    
    glScalef(zoom,zoom,zoom);
    
    switch (current_object) {
		case 0:
			drawSphere(1, MyMaterial);
			break;
		case 1:
			// draw your second primitive object here
			glPushMatrix();
			glScalef(1, 2, 1);
			drawTorus(1, 0.1, 1, Jade);
			glPopMatrix();
			break;
		case 2:
			// draw your first composite object here
            glRotated(-30, 0, 0, 1);
            glRotated(25, 1, 0, 0);
            drawRose();
            break;
		case 3:
			// draw your second composite object here
            glScalef(0.7, 0.7, 0.7);
            drawNecklace();
			break;
		default:
			break;
    };
	glPopMatrix();
	glutSwapBuffers ();
}

void keyboard (unsigned char key, int x, int y)
{
	switch (key) {
        case 'p':
        case 'P':
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            break;
        case 'w':
        case 'W':
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            break;
        case 'v':
        case 'V':
            glPolygonMode(GL_FRONT_AND_BACK,GL_POINT);
            break;
        case 's':
        case 'S':
            m_Smooth = !m_Smooth;
            break;
        case 'h':
        case 'H':
            m_Highlight = !m_Highlight;
            break;
            
        case '1':
        case '2':
        case '3':
        case '4':
            current_object = key - '1';
            break;
            
        case 'Q':
        case 'q':
            exit(0);
            break;
            
        case 'i':
            cy += 0.1;
            break;
            
        case 'k':
            cy -= 0.1;
            break;
        
        case 'j':
            cx -= 0.1;
            break;
            
        case 'l':
            cx += 0.1;
            break;
            
        case 'u':
            ck += 0.1;
            break;
            
        case 'o':
            ck -= 0.1;
            break;
            
        case '[':
            cz += 0.1;
            break;
            
        case ']':
            cz -= 0.1;
            break;
            
        case ',':
            calpha += 5;
            break;
            
        case '.':
            calpha -= 5;
            break;
            
        case '=':
            printf("cx = %.2lf cy = %.2lf cz = %.2lf ck = %.2lf calpha = %.2lf\n", cx, cy, cz, ck, calpha);
            break;
            
        default:
            break;
	}
    
	glutPostRedisplay();
}

void mouse(int button, int state, int x, int y)
{
    if (state == GLUT_DOWN) {
        mouseButton = button;
        moving = 1;
        startx = x;
        starty = y;
    }
    if (state == GLUT_UP) {
        mouseButton = button;
        moving = 0;
    }
}

void motion(int x, int y)
{
    if (moving) {
        if(mouseButton==GLUT_LEFT_BUTTON)
        {
            angle = angle + (x - startx);
            angle2 = angle2 + (y - starty);
        }
        else zoom += ((y-starty)*0.001);
        startx = x;
        starty = y;
        glutPostRedisplay();
    }
    
}

int main(int argc, char **argv)
{
	cout<<"CS3241 Lab 3"<< endl<< endl;
    
	cout << "1-4: Draw different objects"<<endl;
	cout << "S: Toggle Smooth Shading"<<endl;
	cout << "H: Toggle Highlight"<<endl;
	cout << "W: Draw Wireframe"<<endl;
	cout << "P: Draw Polygon"<<endl;
	cout << "V: Draw Vertices"<<endl;
	cout << "Q: Quit" <<endl<< endl;
    
	cout << "Left mouse click and drag: rotate the object"<<endl;
	cout << "Right mouse click and drag: zooming"<<endl;
    
    cx = cy = cz = 0.0;
    ck = calpha = 1.0;
    setMat();
    
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize (600, 600);
	glutInitWindowPosition (50, 50);
	glutCreateWindow ("CS3241 Assignment 3");
	glClearColor (1.0,1.0,1.0, 1.0);
	glutDisplayFunc(display);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutKeyboardFunc(keyboard);
	setupLighting();
	glDisable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
    
    glMatrixMode(GL_PROJECTION);
    gluPerspective( /* field of view in degree */ 40.0,
                   /* aspect ratio */ 1.0,
                   /* Z near */ 1.0, /* Z far */ 80.0);
	glMatrixMode(GL_MODELVIEW);
	glutMainLoop();
    
	return 0;
}